using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Xml;

using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Common;
using Microsoft.TeamFoundation.WorkItemTracking;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Microsoft.TeamFoundation.Controls;

namespace Hemi
{
    public partial class CommitMoveStage : UserControl, IMoveStage
    {
        MoveStageState _state = MoveStageState.OK;
        ProgressDialog _progressDialog;

        public CommitMoveStage()
        {
            InitializeComponent();

            _progressDialog = new ProgressDialog();
            _progressDialog.StartPosition = FormStartPosition.CenterParent;            
        }

        #region IMoveStage Members

        public void CommitChanges()
        {
            //
            // This object sometimes get disposed
            //
            _progressDialog = new ProgressDialog();
            _progressDialog.StartPosition = FormStartPosition.CenterParent;            

            _backgroundWorker.RunWorkerAsync();
            
            _progressDialog._progressLabel.Text = "Moving Work Items...";
            _progressDialog.ShowDialog();

            if (this._moveStateCheckbox.Checked)
            {
                _moveStateBackgroundWorker.RunWorkerAsync();
                _progressDialog.ShowDialog();
            }

            if (this._linkCheckbox.Checked)
            {
                _linkWorkItemsBackgroundWorker.RunWorkerAsync();
                _progressDialog.ShowDialog();
            }
        }

        bool IMoveStage.CanTestValidate
        {
            get
            {
                return false;
            }
        }

        string IMoveStage.Name
        {
            get { return "Commit Move"; }
        }

        string IMoveStage.Description
        {
            get { return @"In this stage, we will do the actual move of the Work Items.  
Please review all of the Work Items that are about to move, and decide how you want to 
deprecate the original Work Items.  After you press 'Next', this operation cannot be rolled back.";}
        }

        string IMoveStage.InvalidMessage
        {
            get { return "Commit Move is invalid"; }
        }

        string IMoveStage.ValidMessage
        {
            get { return Properties.Resources.MissingFields_ValidMessage;}
        }

        MoveStageState IMoveStage.State
        {
            get { return _state;}
        }

        UserControl IMoveStage.UserControl
        {
            get { return this;}
        }

        bool IMoveStage.Validate()
        {
            return true;
        }

        #endregion

        private void CommitMoveStage_Load(object sender, EventArgs e)
        {
            _loadBackgroundWorker.RunWorkerAsync();
            _progressDialog.ShowDialog();
        }

        #region Save Work Items
        private void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            int totalNumber = HemiContextSingleton.Instance.WorkItemsToMove.Count;
            int oneIncrement = (1 / totalNumber) * 100;

            //
            // Do the actual save
            //
            _backgroundWorker.ReportProgress(0, "Getting ready to move Work Items...");
            foreach (WorkItemMoveInfo workItem in HemiContextSingleton.Instance.WorkItemsToMove)
            {
                _backgroundWorker.ReportProgress(oneIncrement, string.Format("Examining {0}", workItem.WorkItem.Title));
                workItem.Copy.Save();
            }
        }

        private void _backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            SafeProgressUpdate(e);
        }
        
        private void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            _progressDialog.Close();
        }
        #endregion

        #region List Work Items
        private void _loadBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            int totalNumber = HemiContextSingleton.Instance.WorkItemsToMove.Count;
            int oneIncrement = (1 / totalNumber) * 100;

            _loadBackgroundWorker.ReportProgress(0, "Loading Work Items...");
            
            //
            // Show the work items we are going to save
            //
            foreach (WorkItemMoveInfo workItem in HemiContextSingleton.Instance.WorkItemsToMove)
            {
                _loadBackgroundWorker.ReportProgress(oneIncrement, string.Format("Loading {0}", workItem.WorkItem.Title));

                C1.Win.C1FlexGrid.Row row = _workItemsFlex.Rows.Add();

                row["id"] = workItem.WorkItem.Id;
                row["title"] = workItem.WorkItem.Title;
                row["newProject"] = workItem.Copy.Project.Name;
            }   
        }

        private void _loadBackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            SafeProgressUpdate(e);
        }

        private void _loadBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            _progressDialog.Close();
        }
        #endregion

        #region Assign to Moved state
        private void _moveStateBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            AssignToMovedState();
        }

        private void AssignToMovedState()
        {
            //
            // We are essentially doing 3 major things
            //
            int progressIncrementPerUnit = 33;

            //
            // Figure out how many different work item types we are dealing
            // with
            //
            Dictionary<WorkItemType, bool> workItemTypes = new Dictionary<WorkItemType, bool>();

            //
            // Figure out how much each work item makes up of our unit
            //
            _moveStateBackgroundWorker.ReportProgress(progressIncrementPerUnit, "Examining Work Item Types being moved");
            foreach (WorkItemMoveInfo workItem in HemiContextSingleton.Instance.WorkItemsToMove)
            {
                if (!workItemTypes.ContainsKey(workItem.WorkItem.Type))
                {
                    workItemTypes.Add(workItem.WorkItem.Type, true);
                }
            }

            _moveStateBackgroundWorker.ReportProgress(progressIncrementPerUnit, "Ensuring each Work Item Type has a 'Moved' state");
            foreach (WorkItemType wit in workItemTypes.Keys)
            {
                EnsureMovedState(wit);
            }

            _moveStateBackgroundWorker.ReportProgress(progressIncrementPerUnit, "Moving Work Items");
            foreach (WorkItemMoveInfo workItem in HemiContextSingleton.Instance.WorkItemsToMove)
            {
                workItem.WorkItem.State = "Moved";
                workItem.WorkItem.Save();
            }
        }

        private void EnsureMovedState(WorkItemType wit)
        {
            XmlDocument xml = wit.Export(false);

            //
            // Do we have a 'Moved' state?
            //
            XmlNodeList statesList = xml.GetElementsByTagName("STATES");

            //
            // There should only be 1
            //
            XmlNode states = statesList[0];

            bool hasMoved = false;
            List<string> otherStates = new List<string>();
            XmlNode movedState = null;

            foreach (XmlNode state in states)
            {
                string stateValue = state.Attributes["value"].Value;
                otherStates.Add(stateValue);

                if (stateValue.Equals("Moved"))
                {
                    hasMoved = true;
                }

                if (movedState == null)
                {
                    //
                    // We might need to create a moved state, so clone the
                    // node now since we have easy access to other state nodes
                    //
                    movedState = state.CloneNode(false);
                }
            }

            if (!hasMoved)
            {
                //
                // Add this state, as well as transitions
                //
                movedState.Attributes["value"].Value = "Moved";
                states.AppendChild(movedState);

                //
                // Add in the necessary transitions
                //                
                XmlNodeList transitionsList = xml.GetElementsByTagName("TRANSITIONS");

                //
                // There should only be 1
                //
                XmlNode transitions = transitionsList[0];

                foreach (string otherState in otherStates)
                {
                    XmlNode newTransition = transitions.ChildNodes[0].CloneNode(false);
                    newTransition.Attributes["from"].Value = otherState;
                    newTransition.Attributes["to"].Value = "Moved";

                    XmlNode reasons = xml.CreateNode(XmlNodeType.Element, "REASONS", newTransition.NamespaceURI);

                    XmlNode defaultReason = xml.CreateNode(XmlNodeType.Element, "DEFAULTREASON", reasons.NamespaceURI);
                    XmlAttribute reasonAttribute = xml.CreateAttribute("value");
                    reasonAttribute.Value = "This work item has been moved.";
                    defaultReason.Attributes.Append(reasonAttribute);

                    reasons.AppendChild(defaultReason);

                    newTransition.AppendChild(reasons);

                    transitions.AppendChild(newTransition);
                }

                wit.Project.WorkItemTypes.Import((XmlElement)xml.FirstChild);
                wit.Store.RefreshCache();
                wit.Store.SyncToCache();
            }
        }

        private void _moveStateBackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            SafeProgressUpdate(e);
        }

        private void _moveStateBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            _progressDialog.Close();
        }
        #endregion

        #region Link Work Items
        private void _linkWorkItemsBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            _linkWorkItemsBackgroundWorker.ReportProgress(0, "Linking source and target work items...");
            int progressIncrementPerUnit = (1 / HemiContextSingleton.Instance.WorkItemsToMove.Count) * 100;

            foreach (WorkItemMoveInfo workItem in HemiContextSingleton.Instance.WorkItemsToMove)
            {
                _linkWorkItemsBackgroundWorker.ReportProgress(progressIncrementPerUnit, string.Format("Linking {0}", workItem.WorkItem.Title));

                WorkItem source = workItem.WorkItem;
                WorkItem target = workItem.Copy;

                RelatedLink source2Target = new RelatedLink(target.Id);
                source2Target.Comment = "Target Work Item";
                
                int source2TargetId = source.Links.Add(source2Target);

                RelatedLink target2Source = new RelatedLink(source.Id);
                target2Source.Comment = "Source Work Item";

                int target2SourceId = target.Links.Add(target2Source);

                source.Save();
                target.Save();

                source.Links[source2TargetId].Comment = "Target Work Item";
                target.Links[target2SourceId].Comment = "Source Work Item";

                source.Save();
                target.Save();
            }
        }

        private void _linkWorkItemsBackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            SafeProgressUpdate(e);
        }

        private void _linkWorkItemsBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            _progressDialog.Close();
        }
        #endregion

        private void SafeProgressUpdate(ProgressChangedEventArgs e)
        {
            int proposedValue = _progressDialog._progressBar.Value + e.ProgressPercentage;

            if (proposedValue <= _progressDialog._progressBar.Maximum)
            {
                _progressDialog._progressLabel.Text = e.UserState.ToString();
                _progressDialog._progressBar.Value = proposedValue;
            }
        }
    }
}
